package com.dhcc.core.framework.exception;

import java.lang.reflect.UndeclaredThrowableException;

import javax.naming.NoPermissionException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.CredentialsException;
import org.apache.shiro.authc.DisabledAccountException;
import org.apache.shiro.session.InvalidSessionException;
import org.apache.shiro.session.UnknownSessionException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.HttpStatus;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.servlet.ModelAndView;

import com.dhcc.core.framework.log.LogManager;
import com.dhcc.core.framework.log.factory.LogTaskFactory;
import com.dhcc.core.framework.security.ShiroKit;
import com.dhcc.core.framework.util.CommonUtil;
import com.dhcc.core.framework.util.HttpContextUtil;

/**
 * 全局的的异常拦截器（拦截所有的控制器）（带有@RequestMapping注解的方法上都会拦截）
 * 
 * @ClassName: GlobalExceptionHandler
 * @Description: TODO
 * @author: cyf
 * @date: 2018年1月2日 上午9:43:45
 */
@ControllerAdvice
public class GlobalExceptionHandler {

    private Logger log = LoggerFactory.getLogger(this.getClass());

    @Value("${sys.backendPath}")
    private String backendPath;

    @Value("${sys.frontPath}")
    private String frontPath;

    @Value("${sys.frontLoginPage}")
    private String frontLoginPage;

    @Value("${sys.backendLoginPage}")
    private String backendLoginPage;

    @Value("${sys.sysFullName}")
    private String sysFullName;
    /**
     * 拦截业务异常
     *
     * @author cyf
     */
    @ExceptionHandler(BizException.class)
    @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
    public ModelAndView sysException(BizException e, HttpServletRequest request, HttpServletResponse response) {
        if (ShiroKit.isAuthenticated() || ShiroKit.getUser() != null) {
            LogManager.me().executeLog(LogTaskFactory.exceptionLog(ShiroKit.getCurrentUserOnline(), e));
        }
        log.error("业务异常:" + e.getMsg());
        return this.renderErrorView(500, "业务异常", e.getMsg());
    }

    /**
     * 用户未登录
     *
     * @author cyf
     */
    @ExceptionHandler(AuthenticationException.class)
    @ResponseStatus(HttpStatus.UNAUTHORIZED)
    public String unAuth(AuthenticationException e, Model model) {
        log.error("用户未登陆：", e);
        model.addAttribute("sysFullName", sysFullName);
        return getLoginPage();
    }

    /**
     * 账号被冻结
     *
     * @author cyf
     */
    @ExceptionHandler(DisabledAccountException.class)
    @ResponseStatus(HttpStatus.UNAUTHORIZED)
    public String accountLocked(DisabledAccountException e, Model model) {
        String username = HttpContextUtil.getRequest().getParameter("username");
        if(isFront()){
            LogManager.me().executeLog(LogTaskFactory.frontLoginFailLog(ShiroKit.getCurrentUserOnline(),username, "账号被冻结"));
        }else{
            LogManager.me().executeLog(LogTaskFactory.backendLoginFailLog(ShiroKit.getCurrentUserOnline(),username, "账号被冻结"));
        }
        model.addAttribute("tips", "账号被冻结");
        model.addAttribute("sysFullName", sysFullName);
        return getLoginPage();
    }

    /**
     * 账号密码错误
     *
     * @author cyf
     */
    @ExceptionHandler(CredentialsException.class)
    @ResponseStatus(HttpStatus.UNAUTHORIZED)
    public String credentials(CredentialsException e, Model model) {
        String username = HttpContextUtil.getRequest().getParameter("username");
        if(isFront()){
            LogManager.me().executeLog(LogTaskFactory.frontLoginFailLog(ShiroKit.getCurrentUserOnline(),username, "账号密码错误"));
        }else{
            LogManager.me().executeLog(LogTaskFactory.backendLoginFailLog(ShiroKit.getCurrentUserOnline(),username, "账号密码错误"));
        }
        model.addAttribute("tips", "账号密码错误");
        model.addAttribute("sysFullName", sysFullName);
        return getLoginPage();
    }

    /**
     * 验证码错误
     *
     * @author cyf
     */
    @ExceptionHandler(InvalidKaptchaException.class)
    @ResponseStatus(HttpStatus.BAD_REQUEST)
    public String credentials(InvalidKaptchaException e, Model model) {
        String username = HttpContextUtil.getRequest().getParameter("username");
        if(isFront()){
            LogManager.me().executeLog(LogTaskFactory.frontLoginFailLog(ShiroKit.getCurrentUserOnline(),username, "验证码错误"));
        }else{
            LogManager.me().executeLog(LogTaskFactory.backendLoginFailLog(ShiroKit.getCurrentUserOnline(),username, "验证码错误"));
        }
        model.addAttribute("tips", "验证码错误");
        model.addAttribute("sysFullName", sysFullName);
        return getLoginPage();
    }

    /**
     * 权限异常
     *
     * @author cyf
     */
    @ExceptionHandler(UndeclaredThrowableException.class)
    @ResponseStatus(HttpStatus.UNAUTHORIZED)
    public ModelAndView credentials(UndeclaredThrowableException e, HttpServletRequest request,
            HttpServletResponse response) {
        HttpContextUtil.getRequest().setAttribute("tip", "权限异常");
        log.error("权限异常!", e);
        return this.renderErrorView(403, "权限异常", e.getMessage());
    }

    /**
     * 无权访问该资源
     *
     * @author cyf
     */
    @ExceptionHandler(NoPermissionException.class)
    @ResponseStatus(HttpStatus.UNAUTHORIZED)
    public ModelAndView noPermission(NoPermissionException e, HttpServletRequest request,
            HttpServletResponse response) {
        HttpContextUtil.getRequest().setAttribute("tip", "无权访问该资源");
        log.error("无权访问该资源!", e);
        return this.renderErrorView(403, "无权访问该资源", e.getMessage());
    }

    /**
     * 拦截未知的运行时异常
     *
     * @author cyf
     */
    @ExceptionHandler(RuntimeException.class)
    @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
    public ModelAndView notFount(RuntimeException e, HttpServletRequest request, HttpServletResponse response) {
        if (ShiroKit.getUser() != null) {
            LogManager.me().executeLog(LogTaskFactory.exceptionLog(ShiroKit.getCurrentUserOnline(), e));
        }
        HttpContextUtil.getRequest().setAttribute("tip", "服务器未知运行时异常");
        log.error("运行时异常:", e);
        return this.renderErrorView(500, "运行时异常", "服务器未知运行时异常");
    }

    /**
     * session失效的异常拦截
     *
     * @author cyf
     * @Date 2017/6/7 21:02
     */
    @ExceptionHandler(InvalidSessionException.class)
    @ResponseStatus(HttpStatus.BAD_REQUEST)
    public String sessionTimeout(InvalidSessionException e, Model model, HttpServletRequest request,
            HttpServletResponse response) {
        model.addAttribute("tips", "session超时");
        model.addAttribute("sysFullName", sysFullName);
        assertAjax(request, response);
        return getLoginPage();
    }

    /**
     * session异常
     *
     * @author cyf
     * @Date 2017/6/7 21:02
     */
    @ExceptionHandler(UnknownSessionException.class)
    @ResponseStatus(HttpStatus.BAD_REQUEST)
    public String sessionTimeout(UnknownSessionException e, Model model, HttpServletRequest request,
            HttpServletResponse response) {
        model.addAttribute("tips", "session超时");
        model.addAttribute("sysFullName", sysFullName);
        assertAjax(request, response);
        return getLoginPage();
    }

    /**
     * 判断当前是前台端访问，返回对应登录页面
     * 
     * @return
     */
    private String getLoginPage() {
        String loginPage = backendLoginPage;
        if (isFront()) {
            loginPage = frontLoginPage;
        } else {
            loginPage = backendLoginPage;
        }
        return loginPage;
    }
    /**
     * 判断当前是否是前台访问
     * @Title: isFront 
     * @Description: TODO
     * @return
     * @return: Boolean
     */
    private Boolean isFront() {
        String contextPath = HttpContextUtil.getRequest().getRequestURI();
        // backendPath 为空时 frontPath一定不为空
        if (CommonUtil.isEmpty(backendPath)) {
            if (contextPath.indexOf(frontPath) > -1) {
                return true;
            } else {
                return false;
            }
        } else {
            if (contextPath.indexOf(backendPath) > -1) {
                return false;
            } else {
                return true;
            }
        }
    }
    

    private void assertAjax(HttpServletRequest request, HttpServletResponse response) {
        if (request.getHeader("x-requested-with") != null
                && request.getHeader("x-requested-with").equalsIgnoreCase("XMLHttpRequest")) {
            // 如果是ajax请求响应头会有，x-requested-with
            response.setHeader("sessionstatus", "timeout");// 在响应头设置session状态
        }
    }

    private ModelAndView renderErrorView(Integer errorCode, String error, String message) {
        ModelAndView view = new ModelAndView("/error");
        view.addObject("isCustom", true);
        view.addObject("status", errorCode);
        view.addObject("error", error);
        view.addObject("message", message);
        return view;
    }

}
